window.HTree = function(options){
    var self = this;

    self.option = options || { };

    self.newNodeText = self.option.newNodeText || '[未命名]';

    self._ensureChildList = function(node, hasChild){
        if (hasChild && !node.next().hasClass('htree-list'))
            node.after('<div class="htree-list"></div>');
        if (!hasChild && node.next().hasClass('htree-list'))
            node.next().remove();
    };

    self._insertChild = function(node, isAppend, text){
        let parentList = node;
        let doRename = text == null;
        text = text || self.newNodeText;

        if (node.hasClass('htree-node')){
            self._ensureChildList(node, true);
            parentList = node.next('.htree-list:eq(0)');
        }

        if (!parentList.hasClass('htree-list') && !parentList.hasClass('htree-root'))
            return null;

        let parentNode = parentList.prev();
        if (!parentNode.hasClass('htree-node'))
            parentNode = null;

        if (typeof(self.option.beforeAdd) === 'function' && self.option.beforeAdd({tree:self, parentNode, parentList, text}) === false){
            if (parentList.children().length === 0)
                parentList.remove();
            return;
        }

        if (parentNode != null){
            let foldIco = parentNode.children('i:first');
            if (!foldIco.hasClass('fa-caret-down'))
                foldIco.click();
            if (!foldIco.hasClass('fa-caret-down'))
                foldIco.addClass('fa-caret-down');
        }

        let html = '<div class="htree-node">' +
            '<i class="fa"></i>' +
            (self.option.checkbox ? '<input type="checkbox" />' : '') +
            (self.option.icon || '') +
            '<input type="text" value="'+text+'"'+(doRename ? '' : 'readonly')+' />' +
            '<span class="fa-stack i-down"><i class="fa fa-level-down fa-rotate-90 fa-stack-1x"></i><i class="fa fa-window-minimize fa-flip-vertical fa-stack-1x"></i></span>' +
            '<span class="fa-stack i-up"><i class="fa fa-level-up fa-rotate-270 fa-stack-1x"></i><i class="fa fa-window-minimize fa-stack-1x"></i></span>' +
            '<i class="fa fa-trash-o"></i>' +
            '</div>';

        parentList[isAppend ? 'append' : 'prepend'](html);
        let newNode = parentList.children('div:'+ (isAppend ? 'last' : 'first'));
        let nameinput = newNode.children('input[type=text]:first');
        nameinput.keydown(function(e){ if (e.which === 13)$(this).blur() });
        if (doRename){
            nameinput.blur(self._firstrenamecb = function(){
                nameinput.unbind('blur', self._firstrenamecb);
                nameinput.prop('readonly', true);
                self._bindFunction(newNode);
                if (typeof(self.option.afterAdd) === 'function')
                    self.option.afterAdd({tree:self, parentNode, parentList, newNode});
            });
            nameinput.select();
        } else {
            self._bindFunction(newNode);
            if (typeof(self.option.afterAdd) === 'function')
                self.option.afterAdd({tree:self, parentNode, parentList, newNode});
        }

        return newNode;
    };

    self.appendChild = function(node, text){
        return self._insertChild(node, true, text);
    };

    self.prependChild = function(node, text){
        return self._insertChild(node, false, text);
    };

    self.deleteNode = function(node){
        if (!node.hasClass('htree-node'))
            return false;
        let parentList = node.parent();

        if (typeof(self.option.beforeDelete) === 'function' && self.option.beforeDelete({tree:self, node}) === false)
            return;

        self._ensureChildList(node, false);
        node.remove();
        if (parentList.hasClass('htree-root')){
            if (typeof(self.option.afterDelete) === 'function')
                self.option.afterDelete({tree:self, parentNode:null, parentList});
            return;
        }
        let parentNode = parentList.prev('.htree-node');
        if (parentList.children().length === 0){
            parentList.remove();
            if (parentNode.length !== 0){
                let foldIco = parentNode.children('i:first');
                foldIco.removeClass('fa-caret-right');
                foldIco.removeClass('fa-caret-down');
            }
            if (typeof(self.option.afterDelete) === 'function')
                self.option.afterDelete({tree:self, parentNode, parentList : null});
            return;
        }
        if (typeof(self.option.afterDelete) === 'function')
            self.option.afterDelete({tree:self, parentNode, parentList});
    };

    self.switchFold = function(node, fold){
        let foldIco = node.children('i:first');
        let doFold = fold === undefined ?  foldIco.hasClass('fa-caret-down') : fold;
        let childList = node.next('.htree-list:eq(0)');

        if (doFold){
            if (typeof(self.option.beforeFold) === 'function' && self.option.beforeFold({tree:self, node}) === false)
                return;

            childList.css('display', 'none');
            if (foldIco.hasClass('fa-caret-down'))
                foldIco.removeClass('fa-caret-down');
            if (!foldIco.hasClass('fa-caret-right'))
                foldIco.addClass('fa-caret-right');

            if (typeof(self.option.afterFold) === 'function')
                self.option.afterFold({tree:self, node});
        }else{
            if (typeof(self.option.beforeUnfold) === 'function' && self.option.beforeUnfold({tree:self, node}) === false)
                return;

            childList.css('display', '');
            if (foldIco.hasClass('fa-caret-right'))
                foldIco.removeClass('fa-caret-right');
            if (!foldIco.hasClass('fa-caret-down'))
                foldIco.addClass('fa-caret-down');

            if (typeof(self.option.afterUnfold) === 'function')
                self.option.afterUnfold({tree:self, node});
        }
    };

    self.getLevel = function(node){
        let level = 0;
        let node_tmp = node;
        while(!node_tmp.hasClass('htree-root') && node_tmp.length > 0){
            node_tmp = node_tmp.parent();
            level++;
        }
        return node_tmp.length > 0 ? level : -1;
    };

    self.getText = function(node){
        return node.children('input[type=text]:first').val();
    };

    self.getParentNode = function(node){
        let parentList = self.getParentList(node);
        if (parentList == null || parentList.hasClass('htree-root'))
            return null;
        let parent = parentList.prev();
        return parent.hasClass('htree-node') ? parent : null;
    };

    self.getParentList = function(node){
        if (node.hasClass('htree-root'))
            return null;
        return node.parent();
    };

    self.getChildNodes = function(node){
        if (node.hasClass('htree-list') || node.hasClass('htree-root'))
            return node.children();
        return node.next('.htree-list').children();
    };

    self.createByPaths = function(rootnode, paths){
        let nodes = [rootnode];
        let lastval = ['root'];
        paths.sort();
        for (let path of paths){
            let routes = path.split('/');
            routes.unshift('root');
            for (let levelid = 1; levelid < routes.length; levelid++){
                if (routes[levelid] === lastval[levelid])
                    continue;
                nodes[levelid] = self.appendChild(nodes[levelid-1], routes[levelid]);
                lastval[levelid] = routes[levelid];
            }
        }
    };

    self.getPath = function(node){
        let node_tmp = node;
        let path = '';
        while(node_tmp != null){
            path = '/' + node_tmp.children('input[type=text]').val() + path;
            node_tmp = self.getParentNode(node_tmp);
        }
        if (path[0] === '/')
            path = path.substr(1);
        return path;
    };

    self.getNodeByPath = function(rootnode, path){
        let routes = path.split('/');
        let node = rootnode;
        for (let route of routes){
            let nodes = self.getChildNodes(node);
            nodes.each(function(){
                if ($(this).children('input[type=text]:first').val() === route){
                    node = $(this);
                    return false;
                }
            });
        }
        return node;
    };

    self._bindFunction = function(treeNode){
        treeNode.children('span.i-down').click(self._appendcb = function(){
            self.appendChild($(this).parent());
        });
        treeNode.children('span.i-up').click(self._prependcb = function(){
            self.prependChild($(this).parent());
        });
        treeNode.children('i.fa-trash-o').click(self._deletecb = function(){
            self.deleteNode($(this).parent());
        });
        treeNode.children('i:first').click(self._switchfoldcb = function(){
            self.switchFold($(this).parent());
        });
        treeNode.children('input[type=text]:first').dblclick(self._startrenamecb = function(){
            let inputSelf = $(this);
            if (typeof(self.option.beforeRename) === 'function' && self.option.beforeRename({tree:self, node:inputSelf.parent()}) === false)
                return;
            inputSelf.attr('readonly', false).select();
        }).blur(self._endrenamecb = function(){
            let inputSelf = $(this);
            inputSelf.attr('readonly', true);
            if (typeof(self.option.afterRename) === 'function')
                self.option.afterRename({tree:self, node:inputSelf.parent()});
        });

        if (self.option.checkbox && self.option.checkboxRecursive){
            treeNode.children('input[type=checkbox]:first').change(self._checkchangecb = function(){
                self.getChildNodes(treeNode).find('input[type=checkbox]:nth-child(2)').prop('checked', $(this).prop('checked'));
            });
        }
    };
};